﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Silvermoon.Controls.Classes;
using Silvermoon.Controls.Interfaces;

namespace Silvermoon.Controls.Sys
{

    /// <summary>
    /// Helper structure to maintain transitioning of states of a <see cref="T:Grid"/>.
    /// </summary>
    internal struct CellState
    {
        public Rectangle Bounds;

        float ScaleX, ScaleY;
        float StretchX, StretchY;

        float RotateX, RotateY, RotateZ;

        public int Alpha;
        public bool Visible;
        private int level;
        private int max;
        private GridCell cell;
        private Rectangle currentBounds;


        public void Initialize(GridCell cell, Rectangle bounds)
        {
            this.cell = cell;
            Alpha = cell.Alpha;
            ScaleX = ScaleY = 1f;
            StretchX = StretchY = 1f;
            currentBounds = bounds;
            Bounds = bounds;
            RotateX = cell.RotateX;
            RotateY = cell.RotateY;
            RotateZ = cell.RotateZ;
            Visible = Alpha > 0 && bounds.Width > 1 && bounds.Height > 1;
        }


        private static void ScaleCell(Rectangle anchorBounds, Rectangle bounds, out float ScaleX, out float ScaleY)
        {
            float w0 = anchorBounds.Width;
            float h0 = anchorBounds.Height;
            if (w0 == 0) w0 = float.MaxValue;
            if (h0 == 0) h0 = float.MaxValue;
            float w = bounds.Width;
            float h = bounds.Height;
            ScaleX = w / w0;
            ScaleY = h / h0;
        }

        /// <summary>
        /// Interpolates two values.
        /// </summary>
        private int Interpolate(int baseValue, int targetValue)
        {
            if (baseValue == targetValue) return baseValue;
            return baseValue + (targetValue - baseValue) * level / max;
        }

        /// <summary>
        /// Interpolates two values.
        /// </summary>
        private float Interpolate(float baseValue, float targetValue)
        {
            if (baseValue == targetValue) return baseValue;
            return baseValue + (targetValue - baseValue) * level / max;
        }

        public void ApplyToControl(Control c)
        {
            c.Alpha = Alpha;
            c.Visible = Visible;
            c.Bounds = Bounds;
            bool isScaled = ScaleX != 1f || ScaleY != 1f;
            bool isStretched = StretchX != 1f || StretchY != 1f;
            int numTrans = 0;
            if (isScaled) numTrans++;
            if (isStretched) numTrans++;
            if (RotateX != 0f) numTrans++;
            if (RotateY != 0f) numTrans++;
            if (RotateZ != 0f) numTrans++;

            if (numTrans > 0)
            {
                Transformation[] trans = new Transformation[numTrans];
                c.Transformations = trans;

                numTrans = 0;
                if (isStretched) trans[numTrans++] = new StretchTransform(StretchX, StretchY, 1f);
                if (isScaled) trans[numTrans++] = new ScaleTransform(ScaleX, ScaleY, 1f);
                if (RotateX != 0f) trans[numTrans++] = new RotateTransform { Angle = RotateX, Axis = RotateAxis.XAxis };
                if (RotateY != 0f) trans[numTrans++] = new RotateTransform { Angle = RotateY, Axis = RotateAxis.YAxis };
                if (RotateZ != 0f) trans[numTrans++] = new RotateTransform { Angle = RotateZ, Axis = RotateAxis.ZAxis };
            }
        }

        /// <summary>
        /// Interpolate this state with another.
        /// </summary>
        /// <param name="state">The cell state to interpolate with.</param>
        /// <param name="level">The level between this and the state to interpolate, where 0 is exact this state and level=max is exact the specified state.</param>
        /// <param name="max">The maximum value for the specified level parameter.</param>
        public void Interpolate(CellState state, int level, int max)
        {
            int startDelay = state.cell.StartDelay;
            int stopDelay = state.cell.StopDelay;

            if (startDelay != 0 || stopDelay != 0)
            {
                level -= startDelay; if (level < 0) level = 0;
                max -= startDelay;
                max -= stopDelay; if (max < 0) max = 0;
                if (level > max) level = max;
            }
            this.level = level;
            this.max = max;
            Alpha = Interpolate(Alpha, state.Alpha);

            Rectangle r2 = state.Bounds;
            Rectangle r1 = Bounds;

            Rectangle r = new Rectangle(
                Interpolate(r1.X, r2.X),
                Interpolate(r1.Y, r2.Y),
                Interpolate(r1.Width, r2.Width),
                Interpolate(r1.Height, r2.Height));

            currentBounds = r;


            float scaleX, scaleY;
            float stretchX, stretchY;
            switch (cell.ResizeMode)
            {
                case CellResizeMode.Resized:
                    Bounds = r;
                    scaleX = scaleY = 1f;
                    stretchX = stretchY = 1f;
                    break;

                case CellResizeMode.Centered:
                    ScaleCell(r1, r2, out scaleX, out scaleY);
                    Bounds = new Rectangle(r.X, r.Y, r1.Width, r1.Height);
                    Bounds.X += (r.Width - r1.Width) / 2;
                    Bounds.Y += (r.Height - r1.Height) / 2;
                    stretchX = stretchY = 1f;
                    break;

                case CellResizeMode.Stretched:
                    ScaleCell(r1, r2, out stretchX, out stretchY);
                    scaleX = scaleY = 1f;
                    Bounds = new Rectangle(r.X, r.Y, r1.Width, r1.Height);
                    break;

                default:
                    throw new NotImplementedException();
            }

            if (state.cell.Minimized)
            {
                float scale = (float)(max - level) / (float)max;
                scaleX *= scale;
                scaleY *= scale;
            }

            ScaleX = Interpolate(ScaleX, scaleX);
            ScaleY = Interpolate(ScaleY, scaleY);
            StretchX = Interpolate(StretchX, stretchX);
            StretchY = Interpolate(StretchY, stretchY);

            RotateX = Interpolate(RotateX, state.RotateX);
            RotateY = Interpolate(RotateY, state.RotateY);
            RotateZ = Interpolate(RotateZ, state.RotateZ);

            Visible = Alpha > 0 && Bounds.Width > 1 && Bounds.Height > 1;

        }

        private void CopyState(CellState state)
        {
            Visible = state.Visible;
            if (Visible)
            {
                Bounds = state.Bounds;
                ScaleX = state.ScaleX;
                ScaleY = state.ScaleY;
                StretchX = state.StretchX;
                StretchY = state.StretchY;
                Alpha = state.Alpha;
            }
        }
    }
}
